package dao

import (
	"OJCenter/dao/mysql"
	"OJCenter/dao/redis"
	"OJCenter/global"
	"OJCenter/idl"
	"encoding/json"
	"errors"
	"strconv"
	"xorm.io/xorm"
)

const ProblemInfoPre = "p:"

type ProblemInfo struct {
	mysql *mysql.MysqlDao
	redis *redis.RedisDao
}

func NewProblemInfo() (*ProblemInfo, error) {
	m, err := mysql.NewMysqlDao()
	if err != nil {
		return nil, err
	}
	r, err := redis.NewRedisDao()
	if err != nil {
		return nil, err
	}
	return &ProblemInfo{
		mysql: m,
		redis: r,
	}, nil
}

func (u *ProblemInfo) GetProblemInfo(pid int64) (*idl.Problem, error) {
	var pro idl.Problem
	PInfoJson, err := u.redis.GetString(ProblemInfoPre + strconv.Itoa(int(pid)))
	if err != nil {
		_, err := u.mysql.Orm.Where("pid=?", pid).Get(&pro)
		if err != nil {
			return nil, err
		}
		j, _ := json.Marshal(pro)
		err = u.redis.SetString(ProblemInfoPre+strconv.Itoa(int(pid)), string(j), u.redis.TTL)
		if err != nil {
			global.Logger.Errorf("problem load redis failed,err:%v", err)
		}
		return &pro, nil
	}
	err = json.Unmarshal([]byte(PInfoJson), &pro)
	if err != nil {
		global.Logger.Errorf("GetProblemInfo unmarshal failed,err:%v", err)
	}
	return &pro, nil
}

func (u *ProblemInfo) DeleteProblemInfo(info *idl.Problem) error {
	if info == nil {
		return errors.New("info is nil")
	}
	_, err := u.redis.DelKey(ProblemInfoPre + strconv.Itoa(int(info.Pid)))
	if err != nil {
		global.Logger.Errorf("problem load redis failed,err:%v", err)
	}
	cond := make(map[string]interface{})
	cond["pid"] = info.Pid
	sum, err := u.mysql.DeleteInfos("problem", &cond)
	if err != nil || sum == 0 {
		return errors.New("delete problem failed")
	}
	return nil
}

func (u *ProblemInfo) UpdateProblemInfo(info map[string]interface{}) error {
	if info == nil {
		return errors.New("info is nil")
	}
	_, err := u.redis.DelKey(ProblemInfoPre + strconv.Itoa(int(info["pid"].(int64))))
	if err != nil {
		global.Logger.Errorf("problem load redis failed,err:%v", err)
	}
	cond := make(map[string]interface{})
	cond["pid"] = info["pid"].(int64)
	sum, err := u.mysql.UpdateInfos("problem", &info, &cond)
	if err != nil || sum == 0 {
		return errors.New("update problem failed")
	}
	return nil
}

func (u *ProblemInfo) InsertProblemInfo(info *idl.Problem) error {
	Problem, err := u.GetProblemInfo(info.Pid)
	if err != xorm.ErrNotExist {
		return err
	}
	sum, err := u.mysql.InsertInfos("problem", Problem)
	if err != nil || sum == 0 {
		return err
	}
	return nil
}

func (u *ProblemInfo) GetProblemList(page int, limit int) (*[]idl.Problem, error) {
	var pros []idl.Problem
	err := u.mysql.Orm.Where("is_visible = ?", true).Limit(limit, (page-1)*limit).Find(&pros)
	if err != nil {
		return nil, err
	}
	return &pros, nil
}

func (u *ProblemInfo) SelectProblems(info *idl.Problem) (*[]idl.Problem, error) {
	var rets []idl.Problem
	err := u.mysql.Orm.Table("problem").Find(&rets, info)
	if err != nil {
		return nil, err
	}
	return &rets, nil
}
